import java.io.Serializable;
import java.util.Random;

/**
 * Event Class: Generates one of ten events if roll correctly, otherwise there is a "MISS"
 */
public class Event implements Serializable{

	/* events: Storm, PlayerIll, PlayerDied, PlayerRecovered, WagonDamaged, ItemStolen, ItemDonated, WagonFlipped, OxDied, OxTired */
	private static final int EVENT_COUNT = 10, HIT_RATIO = 100, HEALTHY = 0, SICK = 1;
	private Random rand;
	
	//private Game game;
	private Equipment eq;        /* needed by events Storm, WagonDamaged, ItemStolen, ItemDonated, WagonFlipped, OxDied, OxTired */
	private PartyMember[] party; /* needed by events PlayerIll, PlayerDied, PlayerRecovered */
	private PartyLeader leader;  /* needed by events PlayerIll, PlayerDied, PlayerRecovered */
	private Wagon wagon;         /* needed by event Storm */
	
	/**
	 * Constructor, makes use of necessary game classes for events to affect game status
	 * @param game    
	 * @param eq      needed by events Storm, WagonDamaged, ItemStolen, ItemDonated, WagonFlipped, OxDied, OxTired
	 * @param party   needed by events PlayerIll, PlayerDied, PlayerRecovered
	 * @param leader  needed by events PlayerIll, PlayerDied, PlayerRecovered
	 * @param wagon   needed by event Storm
	 */
	public Event(Game game, Equipment eq, PartyMember[] party, PartyLeader leader, Wagon wagon)
	{
		rand = new Random();
		
		//this.game = game;
		this.eq = eq;
		this.party = party;
		this.leader = leader;
		this.wagon = wagon;
	}
	
	/**
	 * Generates one of ten events if roll correctly, otherwise there is a "MISS"
	 * @param isRiverEvent  Determines whether to do river event or non river event.
	 * @return Message describing event, to be processed by Game class.
	 */
	public String generateRandomEvent(boolean isRiverEvent)
	{
		if (isRiverEvent)
			return eventWagonFlipped();
		else
			return chooseEvent(rand.nextInt(EVENT_COUNT));
	}
	

	/**
	 * Chooses event contigent on seed calculated in generateRandomEvent and boolean from isHit
	 * @param seed Determines which event is to occur.
	 * @return Message describing event.
	 */
	private String chooseEvent(int seed)
	{
		int roll = rand.nextInt(HIT_RATIO);
		
		if (isHit(roll))
		{
			switch (seed)
			{
				case 0: return eventStorm();
				case 1: return eventPlayerIll();
				case 2: return eventPlayerDied();
				case 3: return eventPlayerRecovered();
				case 4: return eventWagonDamaged();
				case 5: return eventItemStolen();
				case 6: return eventItemDonated();
				case 7: return "MISS"; /* eventWagonFlipped() only occurs at river crossing */
				case 8: return eventOxDied();
				case 9: return eventOxTired();
			}
		}
		return "MISS";
	}
	
	/**
	 * Event 'Event Storm'
	 * @return Message describing event.
	 */
	private String eventStorm()
	{
		int numDays = rand.nextInt(3) + 1;
		int foodDeficit = numDays * wagon.getRations() * peopleAlive();
		int[] foodArray = new int[2];
		foodArray[1] = (0 - foodDeficit);
		eq.addItems(foodArray);
		
		return String.format("Severe storm, lose %d days.", numDays);
		
	}
	
	/**
	 * Event 'Player Ill'
	 * @return Message describing event.
	 */
	private String eventPlayerIll()
	{
		String name = updatePlayer(HEALTHY, SICK);
		
		if (!name.equals("MISS"))
			return String.format("%s is ill.", name);
		else
			return name;
	}
	
	/**
	 * Event 'Player Died'
	 * @return Message describing event.
	 */
	private String eventPlayerDied()
	{
		String name = updatePlayer(SICK, SICK);
		
		//TODO If leader dead, end game.
		if (!name.equals("MISS"))
			return String.format("%s has died.", name);
		else
			return name;
	}
	
	/**
	 * Event 'Player Recovered'
	 * @return Message describing event.
	 */
	private String eventPlayerRecovered()
	{
		String name = updatePlayer(SICK, HEALTHY);
		
		if (!name.equals("MISS"))
			return String.format("%s has recovered.", name);
		else
			return name;
	}
	
	/**
	 * Event 'Wagon Damaged'
	 * @return Message describing event.
	 */
	private String eventWagonDamaged()
	{
		String[] wagonItem = {"Wagon Wheel", "Wagon Axle", "Wagon Tongue"};
		int currentItem = rand.nextInt(wagonItem.length);
		int[] current = eq.getQuantities();
		
		if ((leader.getPlayerProf().toString() == "Carpenter") && (rand.nextInt(HIT_RATIO) < 75))
			return "MISS";
		
		if (current[4 + currentItem] > 0)
		{
			int[] itemArray = new int[7];
			itemArray[4 + currentItem] = -1;
			eq.addItems(itemArray);
			
			return String.format("Wagon was damaged, used %s to fix.", wagonItem[currentItem]);
		}
		else
		{
			//TODO implement game.setCanMove(false); in Game class, "boolean" term.
			return String.format("Wagon is damaged, requires %s to fix.", wagonItem[currentItem]);
		}
	}
	
	/**
	 * Event 'Item Stolen'
	 * @return Message describing event.
	 */
	private String eventItemStolen()
	{
		String[] wagonItem = {"Oxen", "Food", "Clothing", "Ammunition", "Wagon Wheel", "Wagon Axle", "Wagon Tongue"};
		int currentItem = rand.nextInt(wagonItem.length);
		int amountStolen;
		
		if (currentItem > 1 && currentItem < 4)
			amountStolen = rand.nextInt(eq.getQuantities()[currentItem] / 10 + 1) + 1;
		else
			amountStolen = -1;
		
		int[] itemArray = new int[7];
		itemArray[currentItem] = - amountStolen;
		eq.addItems(itemArray);
		
		return String.format("Wandering zombie steals %s from wagon.", wagonItem[currentItem]);
	}
	
	/**
	 * Event 'Item Donated'
	 * @return Message describing event.
	 */
	private String eventItemDonated()
	{
		int foodDonated = rand.nextInt(90) + 11;
		
		int[] foodArray = new int[2];
		foodArray[1] = foodDonated;
		eq.addItems(foodArray);
		
		return String.format("Chuck Norris feels sorry for you, gives you %s food.", foodDonated);
	}
	
	private String eventWagonFlipped()
	{
		return "MISS"; //implemented in Game.move()
	}
	
	/**
	 * Event 'Oxen Died'
	 * @return Message describing event.
	 */
	private String eventOxDied()
	{
		int[] oxenArray = new int[1];
		oxenArray[0] = -1;
		eq.addItems(oxenArray);
		
		//TODO Check for 0 oxen, and end game if this happens.
		return String.format("One of your Oxen has died.");
	}
	
	/**
	 * Event 'Oxen Tired'
	 * @return Message describing event.
	 */
	private String eventOxTired()
	{
		//TODO implement incremental change in pace.
		int reduction = rand.nextInt(3) + 1;
		return String.format("Your Oxen are tired, movement reduced by %s miles per day.", reduction);
	}
	
	/**
	 * Helper method for chooseEvent that determines whether an event will occur or not.
	 * @param roll Value from 0 - 99, if within range there is a hit.
	 * @return Returns true for a hit.
	 */
	private boolean isHit(int roll)
	{
		if (roll >= 75)
			return true;
		else
			return false;
	}
	
	/**
	 * Determines the number of people in the party that are still alive.
	 * @return The number of living party members.
	 */
	private int peopleAlive()
	{
		int peopleAlive = 1;
		for (int i = 0; i < party.length; i++)
		{
			if (party[i].getStatus() != 2)
				peopleAlive++;
		}
		
		return peopleAlive;
	}

	/**
	 * Helper function for the methods eventPlayerIll, eventPlayerDied, eventPlayerrecovered
	 * @param status  Current status necessary for candidates.
	 * @param newStatus  New status of candidates.
	 * @return  Name of candidate chosen.
	 */
	public String updatePlayer(int status, int newStatus)
	{
		boolean foundStatus = false;
		String name = "MISS";
		
		for (int i = party.length - 1; (!foundStatus) && (i >= 0); i--)
		{
			if (party[i].getStatus() == status)
			{
				foundStatus = true;
				name = party[i].getName();
				party[i].setStatus(newStatus); //updates status to new status
			}
		}
		if (!foundStatus)
		{
			if (leader.getStatus() == status)
			{
				name = leader.getName();
				leader.setStatus(newStatus);
			}
		}
		return name;
	}
	
}
